package models

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/httplib"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
	qrcode "github.com/skip2/go-qrcode"
)

const Debug = true

// 注册数据库
func init() {
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", "root:1qazCDE#@tcp(47.99.32.40)/tcsc?charset=utf8&&loc=Local")
	//orm.RegisterDataBase("default", "mysql", "root:123456ab@/tcsc?charset=utf8&&loc=Local")
	orm.RegisterModel(new(Mt_resources))
	orm.RegisterModel(new(Mt_systems_record))
	o := orm.NewOrm()
	o.Using("default") // 默认使用 default
	System = Systems()
}

type Mt_resources struct {
	Id       int       `orm:"pk" form:"id"`
	Uid      int       `form:"uid"`
	Type     int       `form:"type"`
	Path     string    `form:"path"`
	Name     string    `form:"name"`
	Pathname string    `form:"pathname"`
	Addtime  time.Time `form:"addtime" orm:"type(datetime)"`
	Ip       string    `form:"ip"`
}
type Mt_systems_record struct {
	Id      int `orm:"pk" form:"id"`
	Uid     int
	Table   int
	Way     int
	Tid     int
	Addtime time.Time `orm:"type(datetime)"`
}

// 全局变量
var System orm.Params

// 打开日记文件
func ormlog() *os.File {
	timestamp11 := time.Now().Unix()
	tm := time.Unix(timestamp11, 0)
	dirname := tm.Format("2006-01-02 15")
	dirpath := "./logs/" + dirname
	if errdir := os.MkdirAll(dirpath, 0777); errdir != nil {
		beego.Debug(errdir)
	}
	files, errfile := os.OpenFile(dirpath+"/logs.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
	if errfile != nil {
		beego.Debug(errfile)
	}
	return files
}

// 分页

func Systems() orm.Params {
	o := orm.NewOrm()
	var maps []orm.Params
	_, err := o.Raw(`select a.*,b.pathname,c.pathname as apathname,d.pathname as wpathname,z.name as shen,x.name as shi,v.name as qu,
		m.pathname as gzhpath,n.pathname as apppath,l.pathname as webpath,k.pathname as xapppath from mt_systems a left join mt_resources
		 b on a.logo=b.id left join mt_resources c on a.appqrcode=c.id left join mt_resources d on a.wechatqrcode=d.id left join mt_area z 
		 on a.province=z.code left join mt_area x on a.city=x.code left join mt_area v on a.area=v.code left join mt_resources m on a.gzhimg=m.id 
		 left join mt_resources n on a.appimg=n.id left join mt_resources l on a.webimg=l.id left join mt_resources k on a.xappimg=k.id`).Values(&maps)
	if err != nil {
		beego.Debug(err)
	}
	return maps[0]
}

// 使用默认数据库
func Orm() orm.Ormer {
	orm.Debug = Debug
	if Debug {
		sqlfile()
	}
	o := orm.NewOrm()
	return o
}

var LogSetNum = 0

func sqlfile() {
	filename := time.Now().Local().Format("2006-01-02")
	filepath := "logs/" + filename + "/sql" + beego.BConfig.RunMode + ".log"
	isexist := CheckFileExist(filepath)
	if isexist {
		if LogSetNum == 0 {
			file, err := os.OpenFile(filepath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
			if err != nil {
				beego.Error(err)
			}
			LogSetNum++
			orm.DebugLog = orm.NewLog(file)
		}
	} else {
		err := os.MkdirAll("logs/"+filename, 0777)
		if err != nil {
			beego.Error(err)
		}
		f, err := os.Create(filepath)
		if err != nil {
			beego.Error(err)
		}
		defer f.Close()
		orm.DebugLog = orm.NewLog(f)
	}
}

func CheckFileExist(f string) bool {
	_, err := os.Stat(f)
	return err == nil || os.IsExist(err)
}

// 执行sql语句
func Query(a string, args ...interface{}) (maps []orm.Params) {
	files := ormlog()
	defer files.Close()
	orm.DebugLog = orm.NewLog(files)
	orm.Debug = true
	o := Orm()
	_, err := o.Raw(a, args...).Values(&maps)
	if err != nil {
		beego.Debug(err)
	}
	return
}

// 执行 update delete操作
func Exec(a string, args ...interface{}) error {
	files := ormlog()
	defer files.Close()
	orm.DebugLog = orm.NewLog(files)
	orm.Debug = true
	o := Orm()
	_, err := o.Raw(a, args...).Exec()
	if err != nil {
		beego.Debug("exec bug:", err)
	}
	return err
}
func QueryBulid() orm.QueryBuilder {
	querybuild, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		beego.Debug()
		return querybuild
	}
	return querybuild
}

// 数据库插入修改
func Save(table string, data url.Values) (num int64, err error) {
	if table == "" {
		err = errors.New("表名不能为空")
		num = 0
		return
	}
	if data == nil {
		err = errors.New("表名不能为空")
		num = 0
		return
	}
	files := ormlog()
	defer files.Close()
	orm.DebugLog = orm.NewLog(files)
	orm.Debug = true
	o := Orm()
	sql := ""
	var id = data.Get("id")
	if id == "" {
		var field []string
		var value []string
		for k, v := range data {
			if v[0] != "" {
				field = append(field, "`"+k+"`")
				value = append(value, "'"+v[0]+"'")
			}
		}
		field_str := strings.Join(field, ",")
		value_str := strings.Join(value, ",")
		sql = "insert into " + table + "(" + field_str + ") values(" + value_str + ")"
	} else {
		var set []string
		for k, v := range data {
			if v[0] != "" {
				set = append(set, "`"+k+"`='"+v[0]+"'")
			} else {
				set = append(set, "`"+k+"`=null")
			}
		}
		set_str := strings.Join(set, ",")
		sql = "update " + table + " set " + set_str + " where id=" + id
	}
	res, err2 := o.Raw(sql).Exec()
	// beego.Debug(sql)
	if err2 != nil {
		err = err2
		num = 0
	} else {
		if id == "" {
			num, err = res.LastInsertId()
		} else {
			num, err = strconv.ParseInt(id, 10, 64)
		}
	}
	return
}

// 获取微信推送
type AccessToken struct {
	Access_token string `json:"access_token"`
	Expires_in   int    `json:"expires_in"`
	Errcode      int    `json:"errcode"`
	Errmsg       string `json:"errmsg"`
}

func Push(touser string, template_id string, data interface{}, uri string, topcolor string) string {
	access_token := GetAccessToken("")
	url2 := "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token.Access_token
	//模板消息
	if topcolor == "" {
		topcolor = "#7B68EE"
	}
	var push = make(map[string]interface{})
	push["touser"] = touser
	push["template_id"] = template_id
	push["url"] = uri
	push["topcolor"] = topcolor
	push["data"] = data
	res, _ := json.Marshal(push)
	req := httplib.Post(url2)
	req.Body(string(res))
	result, _ := req.String()
	return result
}

type Ticket struct {
	Errcode    int    `json:"errcode"`
	Errmsg     string `json:"errmsg"`
	Ticket     string `json:"ticket"`
	Expires_in int    `json:"expires_in"`
}

var (
	Urlc         = beego.AppConfig.String("url")
	AppID        = beego.AppConfig.String("wx_appid")
	AppSecret    = beego.AppConfig.String("wx_secret")
	WxPay_mchid  = beego.AppConfig.String("wxpay_mchid")
	WxPay_secret = beego.AppConfig.String("wxpay_secret")
)

type WxToken struct {
	Timestamp1   int64  `json:"timestamp1"`
	Timestamp    int64  `json:"timestamp"`
	Access_token string `json:"access_token"`
	Ticket       string `json:"ticket"`
	Appid        string `json:"appid"`
	Signature    string `json:"signature"`
	NonceStr     string `json:"nonceStr"`
}

func GetAccessToken(add string) WxToken {
	timestamp2 := time.Now().Unix()
	bytes, err := ioutil.ReadFile("token/wxtoken.json")
	if err != nil {
		beego.Debug("ReadFile: ", err)
	}
	var xxx WxToken
	if err := json.Unmarshal(bytes, &xxx); err != nil {
		beego.Debug("Unmarshal: ", err)
	}
	if xxx.Timestamp1 < (timestamp2 - 290) {
		// beego.Debug("in")
		url4 := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppID + "&secret=" + AppSecret
		req1 := httplib.Get(url4)
		m1 := new(AccessToken)
		req1.ToJSON(&m1)
		// beego.Debug(m1)
		url5 := "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" + m1.Access_token
		req2 := httplib.Get(url5)
		m2 := new(Ticket)
		req2.ToJSON(&m2)
		// beego.Debug(m2)
		xxx.Ticket = m2.Ticket
		xxx.Access_token = m1.Access_token
		xxx.Timestamp1 = timestamp2
	}
	xxx.Appid = AppID
	timestamp := strconv.FormatInt(timestamp2, 10)
	nonceStr := RandomStr(16)
	str := "jsapi_ticket=" + xxx.Ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + Urlc + add
	signature := sha1s(str)
	xxx.Signature = signature
	xxx.NonceStr = nonceStr
	xxx.Timestamp = timestamp2
	arr2, _ := json.Marshal(xxx)
	if writeerr := ioutil.WriteFile("token/wxtoken.json", arr2, 0777); writeerr != nil {
		beego.Debug(writeerr)
	}
	return xxx

}

func ReturnId() string {
	year := strconv.Itoa(time.Now().Year())
	month := time.Now().YearDay()/30 + 1
	var month2 string
	if month < 10 {
		month2 = "0" + strconv.Itoa(month)
	} else {
		month2 = strconv.Itoa(month)
	}
	day := strconv.Itoa(time.Now().Day())
	var rands10 string
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < 10; i++ {
		x := rand.Intn(10)
		rands10 += strconv.Itoa(x)
	}
	strs := year + month2 + day + rands10
	return strs
}

// RandomStr 随机生成字符串
func RandomStr(length int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < length; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

func sha1s(s string) string {
	r := sha1.Sum([]byte(s))
	return hex.EncodeToString(r[:])
}

// 清除日记
func ClearLog() {
	go func() {
		for {
			now := time.Now() // 计算下一个零点
			next := now.Add(time.Hour * 24)
			next = time.Date(next.Year(), next.Month(), next.Day(), next.Hour(), next.Minute(), next.Second(), 0, next.Location())
			t := time.NewTimer(next.Sub(now))
			<-t.C
			ClearS()
		}
	}()
}
func ClearS() {
	file, err := os.Open("./logs")
	if err != nil {
		beego.Debug(err)
	}
	dirnames, err2 := file.Readdirnames(-1)
	if err2 != nil {
		beego.Debug(err2)
	}
	beego.Debug(dirnames)
	now := time.Now().Unix() - 259200
	for _, v := range dirnames {
		times, err3 := time.Parse("2006-01-02 15", v)
		if err3 != nil {
			beego.Debug("时间字符砖转换时间戳失败:", err3)
		}
		t := times.Unix()
		if t < now {
			err4 := os.RemoveAll("./logs/" + v)
			if err4 != nil {
				beego.Debug("删除文件失败:", err4)
			}
		}
	}
	defer file.Close()
}

// 保留两位小数
func FloatToFloat(a float64) float64 {
	b := fmt.Sprintf("%.2f", a)
	one, _ := strconv.ParseFloat(b, 64)
	return one
}
func Pagechange(now string, num string) (page string, page2 int, nums string) {
	if now == "" {
		now = "1"
	}
	if num == "" {
		num = "10"
	}
	page2, _ = strconv.Atoi(now)
	numint, _ := strconv.Atoi(num)
	pages := (page2 - 1) * numint
	page = strconv.Itoa(pages)
	nums = num
	return
}

// 匹配数据库 返回年月
func Date() string {
	cur := time.Now().Format("2006-01-02")
	year := time.Now().Year()
	year2 := strconv.Itoa(year)
	cur2, _ := time.Parse("2006-01-02", cur)
	month := int(cur2.Month())
	var month2 string
	if month != 11 && month != 12 && month != 10 {
		month2 = "0" + strconv.Itoa(month)
	}
	var s []string
	s = append(s, year2)
	s = append(s, month2)
	date := strings.Join(s, "-")
	return date
}
func Page(sql string, page2 int, nums string) map[string]interface{} {
	totals := Query(sql)[0]["totals"].(string)
	total, _ := strconv.ParseInt(totals, 10, 64)
	num, _ := strconv.Atoi(nums)
	var paginator = make(map[string]interface{})
	if total == 0 {
		paginator = Paginator(1, num, int64(num))
	} else {
		paginator = Paginator(page2, num, total)
	}
	return paginator
}
func Paginator(page, prepage int, nums int64) map[string]interface{} {

	var firstpage int //第一页地址
	var lastpage int  //最后一页地址
	var nextpage int  //后一页地址
	var prevpage int  //前一页地址
	//根据nums总数，和prepage每页数量 生成分页总数
	totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //page总数
	if page > totalpages {
		page = totalpages
	}
	if page <= 0 {
		page = 1
	}
	var pages []int
	switch {
	case page > totalpages-5 && totalpages > 5: //最后5页
		start := totalpages - 5 + 1
		prevpage = page - 1
		firstpage = 1
		lastpage = totalpages
		nextpage = int(math.Min(float64(totalpages), float64(page+1)))
		pages = make([]int, 5)
		for i, _ := range pages {
			pages[i] = start + i
		}
	case page >= 3 && totalpages > 5:
		start := page - 3 + 1
		pages = make([]int, 5)
		prevpage = page - 3
		firstpage = 1
		lastpage = totalpages
		for i, _ := range pages {
			pages[i] = start + i
		}
		prevpage = page - 1
		nextpage = page + 1
	default:
		pages = make([]int, int(math.Min(5, float64(totalpages))))
		for i, _ := range pages {
			pages[i] = i + 1
		}
		firstpage = 1
		lastpage = totalpages
		prevpage = int(math.Max(float64(1), float64(page-1)))
		nextpage = page + 1
		//fMt.Println(pages)
	}
	paginatorMap := make(map[string]interface{})
	paginatorMap["pages"] = pages           //页数
	paginatorMap["totalpages"] = totalpages //总页数
	paginatorMap["firstpage"] = firstpage   //首页
	paginatorMap["lastpage"] = lastpage     //末页
	paginatorMap["prevpage"] = prevpage     //上一页
	paginatorMap["nextpage"] = nextpage     //下一页
	paginatorMap["currpage"] = page
	return paginatorMap
}

func GetQr(url string, path string) error {
	err := qrcode.WriteFile(url, qrcode.Medium, 256, path)
	return err
}
